package com.youlai.quarkus.system.aspect;

import cn.hutool.core.util.StrUtil;
import com.youlai.quarkus.system.common.anno.annotation.PreventDuplicateSubmit;
import com.youlai.quarkus.system.common.exception.BusinessException;
import com.youlai.quarkus.system.common.redis.IRedisService;
import com.youlai.quarkus.system.common.result.ResultCode;
import com.youlai.quarkus.system.security.jwt.JwtTokenProvider;
import io.vertx.ext.web.RoutingContext;
import jakarta.interceptor.AroundInvoke;
import jakarta.interceptor.Interceptor;
import jakarta.interceptor.InvocationContext;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * Quarkus 是一个为构建 Kubernetes 原生 Java 应用而设计的框架，
 * 它主要利用了 CDI（Contexts and Dependency Injection）以及 SmallRye 等扩展来实现依赖注入和扩展功能
 *
 * @since 2.3.0
 */
@Interceptor
@Slf4j
@RequiredArgsConstructor
@PreventDuplicateSubmit
public class DuplicateSubmitAspect {

    private final IRedisService redisService;
    private final JwtTokenProvider jwtTokenProvider;


    /*
        1. @Context注解 在资源类（即带有@Path注解的类）中，
            你可以使用@Context注解来注入UriInfo、HttpServletRequest、SecurityContext等JAX-RS特定的上下文对象。
         2. RoutingContext。使用Quarkus的默认JAX-RS实现（RESTEasy Reactive），则可以通过注入RoutingContext来访问请求上下文。
            但是，请注意，RoutingContext是Vert.x Web和RESTEasy Reactive特有的，并且与Jersey的ContainerRequestContext不兼容。
     */
    private final RoutingContext context;



    private static final String RESUBMIT_LOCK_PREFIX = "LOCK:RESUBMIT:";

    /**
     * 防重复提交切点
     */
    public void preventDuplicateSubmitPointCut() {
        log.info("定义防重复提交切点");
    }

    @AroundInvoke
    public Object doAround(InvocationContext ctx) throws Exception {
        preventDuplicateSubmitPointCut();
        String resubmitLockKey = generateResubmitLockKey();
        PreventDuplicateSubmit preventDuplicateSubmit = ctx.getInterceptorBinding(PreventDuplicateSubmit.class);
        if (resubmitLockKey != null) {
            // 防重提交锁过期时间
            int expire = preventDuplicateSubmit.expire();
            boolean lockResult = redisService.tryLock(resubmitLockKey, expire);
            // 抛出重复提交提示信息
            if (!lockResult) {
                throw new BusinessException(ResultCode.REPEAT_SUBMIT_ERROR);
            }
        }
        return ctx.proceed();
    }


    /**
     * 获取重复提交锁的 key
     */
    private String generateResubmitLockKey() {
        String resubmitLockKey = null;
        String bearerToken = context.request().getHeader("Authorization");
        String[] split = bearerToken.split(" ");
        String token = split[1];
        if (StrUtil.isNotBlank(token)) {
            String jti = (String) jwtTokenProvider.getTokenClaims(token).getClaim("jti");
            resubmitLockKey = RESUBMIT_LOCK_PREFIX + jti + ":" + context.request().method() + "-" + context.request().path();
        }
        return resubmitLockKey;
    }

}
